package org.zerta.troops.client;

import java.io.File;
import java.io.IOException;
import java.lang.Integer;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics; 
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;

import javax.swing.event.MouseInputListener;

import org.zerta.troops.common.Area;
import org.zerta.troops.common.World;


public class MapPanel extends JPanel implements Scrollable
{
	static final long serialVersionUID = 1L;

    // Our access point to the outside world
    private GameController controller;
    
    // Graphical map data
    private Image image;
    private double zoom;
    
    /** Handler for mouse input on map. TODO: Is it a good design to write multiple such classes for different application states? */
    class MapInputHandler implements MouseInputListener, KeyListener
    {
        public void keyPressed(KeyEvent e) {
        }

        public void keyReleased(KeyEvent e) {
        }

        public void keyTyped(KeyEvent e) {
        }

        public void mouseMoved(MouseEvent e)
        {
        	World world = controller.getWorld();

        	int x = e.getX();
            int y = e.getY();

            // Find closest country (very slow algorithm!)
            int distance = Integer.MAX_VALUE;
            String best = "";
            for(Area c: world.getAreas().values())
            {
                int d = (c.getPosition().x-x)*(c.getPosition().x-x) + (c.getPosition().y-y)*(c.getPosition().y-y);
                if(d < distance)
                {
                    distance = d;
                    best = c.getName();
                }
            }
            setHighlight(best);
        }
        
        public void mousePressed(MouseEvent e) {
            //System.out.println("Pressed area " + controller.getHighlightedArea());
            setDragged(controller.getHighlightedArea());
        }

        public void mouseReleased(MouseEvent e) {
            //System.out.println("Released area " + controller.getDraggingArea());
            setDragged("");
        }

        public void mouseClicked(MouseEvent e) {
            //System.out.println("Clicked area " + controller.getHighlightedArea());
        }

        public void mouseDragged(MouseEvent e) {
            //System.out.println("Dragging area " + controller.getDraggingArea());
        }

        public void mouseEntered(MouseEvent e) {
            //System.out.println("Entered.");
        }

        public void mouseExited(MouseEvent e) {
            //System.out.println("Exited.");
            setHighlight("");
        }
    };

    // Input handlers
    private MapInputHandler mapInputHandler;

    /**
     * Construct a panel showing the game map.
     * @param c
     */
    public MapPanel(GameController c) {
        controller = c;
        
        // Load map image
        // TODO: Get filename from somewhere
        String mapImageFilename = "resources/images/Colors.png";
        try {
            image = ImageIO.read(new File(mapImageFilename));
        } catch (IOException e) {
        	throw new RuntimeException("Failed loading image!");
        }
        
        zoom = 1.0 / 2.0;
        
        // Input handlers are implemented in inner class MapInputHandler
        mapInputHandler = new MapInputHandler();
        addMouseListener(mapInputHandler);
        addMouseMotionListener(mapInputHandler);

        // Appearance
        setBorder(BorderFactory.createLineBorder(Color.red));
        setBackground(Color.white);
    }

    // TODO: Remove when GameController can trigger repaints here.
    public void setHighlight(String name)
    {
    	controller.setHighlightedArea(name);
        repaint();
    }
    
    // TODO: Remove when GameController can trigger repaints here.
    public void setDragged(String name)
    {
    	controller.setDraggingArea(name);
        repaint();
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        Graphics2D g2 = (Graphics2D)g;
        AffineTransform transform = AffineTransform.getScaleInstance(zoom, zoom);
        g2.setTransform(transform);

        // Draw background map image
        // TODO: Highlight selected country using mask.
        // TODO: Draw borders clearly.
        // TODO: Apply noise textures to country masks?
        g2.drawImage(image, 0, 0, null);

        // Draw map info on top
    	World world = controller.getWorld();
        for(Area area: world.getAreas().values())
        {
        	// Get area status
        	String name = area.getName();
        	boolean isDragged = name == controller.getDraggingArea();
            boolean isHighligthed = name == controller.getHighlightedArea();
            String owner = "Unknown owner"; //area.getOwner().getName();
            Point pos = area.getPosition();

            // Draw background box
            if(isDragged) g2.setColor(Color.GRAY);
            else g2.setColor(Color.WHITE);
            // TODO: Compute better sizes. Make infobox a JLabel subclass perhaps?
            int w = 200;
            int h = 60;
            int x = pos.x - w/2;
            int y = pos.y - h/2;
            g2.fillRect(x, y, w, h);
            g2.setColor(Color.BLACK);
            g2.drawRect(x, y, w, h);

            // Print text
            // TODO: Improve formatting of text. 
            // TODO: Print more info.
            if(isHighligthed) g2.setColor(Color.RED);
            else g2.setColor(Color.BLACK);
            g2.drawString(name, x, y);
            g2.drawString(owner, x, y + h/2);
            
            // TODO: Draw indicator graphics for area owner.
            // TODO: Draw indicator graphics for troop sizes.
            // TODO: Draw indicator graphics for troop movements.
        }
    }
    
    public Dimension mapSize() {
        int width  = image.getWidth(null);
        int height = image.getHeight(null);
		return new Dimension(width, height);
    }

    public Dimension getPreferredSize() {
    	Dimension d = mapSize();
    	d.width *= zoom;
    	d.height *= zoom;
    	return d;
    }

	@Override
	public Dimension getPreferredScrollableViewportSize() {
		return getPreferredSize();
	}

	@Override
	public int getScrollableUnitIncrement(Rectangle visibleRect,
			int orientation, int direction) {
		Dimension d = mapSize();
		if (orientation == SwingConstants.HORIZONTAL)
			return d.width / 100;
		else
			return d.height / 100;
	}

	@Override
	public int getScrollableBlockIncrement(Rectangle visibleRect,
			int orientation, int direction) {
		Dimension d = mapSize();
		if (orientation == SwingConstants.HORIZONTAL)
			return d.width / 10;
		else
			return d.height / 10;
	}

	@Override
	public boolean getScrollableTracksViewportHeight() {
		return false; // I think this is ok
	}

	@Override
	public boolean getScrollableTracksViewportWidth() {
		return false; // I think this is ok
	}
}
